
//-------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
#else
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/time.h>

#ifndef __APPLE__
#include <sys/sysinfo.h>
#include <unistd.h>
#endif
#endif

#include "miniutil/thread.h"
#include "miniutil/md5.h"
#include "miniutil/uuid.h"

using namespace miniutil;

/* various forward declarations */
static int read_state(unsigned16 *clockseq, uuid_time_t *timestamp, uuid_node_t * node);
static void write_state(unsigned16 clockseq, uuid_time_t timestamp, uuid_node_t node);
static void get_current_time(uuid_time_t * timestamp);
static unsigned16 true_random(void);
static void get_ieee_node_identifier(uuid_node_t *node);
static void get_system_time(uuid_time_t *uuid_time);
static void get_random_info(char seed[16]);
//#define UUID_SAVE_STATE

//-------------static function---------------------------------------------
/* data type for UUID generator persistent state */
typedef struct {
	uuid_time_t ts;       /* saved timestamp */
	uuid_node_t node;     /* saved node ID */
	unsigned16 cs;        /* saved clock sequence */
} uuid_state;

static uuid_state st;

/* read_state -- read UUID generator state from non-volatile store */
int read_state(unsigned16 *clockseq, uuid_time_t *timestamp, uuid_node_t *node) 
{
	FILE * fd;
	static int inited = 0;

  /* only need to read state once per boot */
	if (!inited) 
	{
		fd = fopen("state", "rb");
		if (!fd)
		   return (0);
		fread(&st, sizeof(uuid_state), 1, fd);
		fclose(fd);
		inited = 1;
	};
	*clockseq = st.cs;
	*timestamp = st.ts;
	*node = st.node;
	return(1);
};

/* write_state -- save UUID generator state back to non-volatile storage */
void write_state(unsigned16 clockseq, uuid_time_t timestamp, uuid_node_t node) 
{
#ifdef UUID_SAVE_STATE
	FILE * fd;
#endif
	static int inited = 0;
	static uuid_time_t next_save;

	if (!inited) 
	{
		next_save = timestamp;
		inited = 1;
	};
	/* always save state to volatile shared state */
	st.cs = clockseq;
	st.ts = timestamp;
	st.node = node;
	if (timestamp >= next_save) 
	{

#ifdef UUID_SAVE_STATE
		fd = fopen("state", "wb");
		fwrite(&st, sizeof(uuid_state), 1, fd);
		fclose(fd);
#endif
	/* schedule next save for 10 seconds from now */
		next_save = timestamp + (10 * 10 * 1000 * 1000);
	};
};

/* get-current_time -- get time as 60 bit 100ns ticks since whenever.
  Compensate for the fact that real clock resolution is less than 100ns. */
void get_current_time(uuid_time_t * timestamp) 
{
	uuid_time_t time_now;
	static uuid_time_t  time_last;
	static unsigned16 uuids_this_tick;
	static int inited = 0;

	if (!inited) {
		get_system_time(&time_now);
		uuids_this_tick = UUIDS_PER_TICK;
		inited = 1;
	};

	while (1) 
	{
		get_system_time(&time_now);

		/* if clock reading changed since last UUID generated... */
		if (time_last != time_now) 
		{
			/* reset count of uuids gen'd with this clock reading */
			uuids_this_tick = 0;
			break;
		};
		if (uuids_this_tick < UUIDS_PER_TICK) 
		{
			uuids_this_tick++;
			break;
		};
		/* going too fast for our clock; spin */
	};
	/* add the count of uuids to low order bits of the clock reading */
	*timestamp = time_now + uuids_this_tick;
};

/* true_random -- generate a crypto-quality random number.
   This sample doesn't do that. */
static unsigned16 true_random(void)
{
  static int inited = 0;
  uuid_time_t time_now;

  if (!inited) 
  {
      get_system_time(&time_now);
      time_now = time_now/UUIDS_PER_TICK;
      srand((unsigned int)(((time_now >> 32) ^ time_now)&0xffffffff));
      inited = 1;
  };

    return (rand());
}


//#define UUID_SAVE_NODE_ID
/* system dependent call to get IEEE node ID.
   This sample implementation generates a random node ID
   */
void get_ieee_node_identifier(uuid_node_t *node) {
  char seed[16];
  FILE * fd;
  static int inited = 0;
  static uuid_node_t saved_node;

  if (!inited) {
      fd = fopen("nodeid", "rb");
      if (fd) 
	  {
           fread(&saved_node, sizeof(uuid_node_t), 1, fd);
           fclose(fd);
      }
      else 
	  {
           get_random_info(seed);
           seed[0] |= 0x80;
           memcpy(&saved_node, seed, sizeof(uuid_node_t));

#ifdef UUID_SAVE_NODE_ID  
		 fd = fopen("nodeid", "wb");
           if (fd) {
                  fwrite(&saved_node, sizeof(uuid_node_t), 1, fd);
                  fclose(fd);
           };
#endif

      };
      inited = 1;
  };

  *node = saved_node;
};

 /* system dependent call to get the current system time.
     Returned as 100ns ticks since Oct 15, 1582, but resolution may be
     less than 100ns.
  */
void get_system_time(uuid_time_t *uuid_time)
{
#ifdef _WINDOWS_
	ULARGE_INTEGER time;

	GetSystemTimeAsFileTime((FILETIME *)&time);

	/* NT keeps time in FILETIME format which is 100ns ticks since
	Jan 1, 1601.  UUIDs use time in 100ns ticks since Oct 15, 1582.
	The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec)
	+ 18 years and 5 leap days.
	*/

	time.QuadPart +=
		(unsigned __int64) (1000*1000*10)       // seconds
	  * (unsigned __int64) (60 * 60 * 24)       // days
	  * (unsigned __int64) (17+30+31+365*18+5); // # of days

    *uuid_time = time.QuadPart;
#else
	struct timeval tp;

	gettimeofday(&tp, (struct timezone *)0);

	/* Offset between UUID formatted times and Unix formatted times.
	 UUID UTC base time is October 15, 1582.
	 Unix base time is January 1, 1970.
	*/
	*uuid_time = (tp.tv_sec * 10000000) + (tp.tv_usec * 10) + I64(0x01B21DD213814000);
#endif        
}


void get_random_info(char seed[16])
{
#ifdef _WINDOWS_
    typedef struct {
        MEMORYSTATUS m;
        SYSTEM_INFO s;
        FILETIME t;
        LARGE_INTEGER pc;
        DWORD tc;
        DWORD l;
        char hostname[MAX_COMPUTERNAME_LENGTH + 1];
    } randomness;
    randomness r;
    /* memory usage stats */
    GlobalMemoryStatus(&r.m);
    /* random system stats */
    GetSystemInfo(&r.s);
    /* 100ns resolution (nominally) time of day */
    GetSystemTimeAsFileTime(&r.t);
    /* high resolution performance counter */
    QueryPerformanceCounter(&r.pc);
    /* milliseconds since last boot */
    r.tc = GetTickCount();
    r.l = MAX_COMPUTERNAME_LENGTH + 1;
    GetComputerName(r.hostname, &r.l );    
#elif defined __APPLE__
    typedef struct {
        struct timeval t;
        char hostname[257];
    } randomness;
    randomness r;
    gettimeofday(&r.t, (struct timezone *)0);
    gethostname(r.hostname, 256);
#else
    typedef struct {
        struct sysinfo s;
        struct timeval t;
        char hostname[257];
    } randomness;
    randomness r;
    sysinfo(&r.s);
    gettimeofday(&r.t, (struct timezone *)0);
    gethostname(r.hostname, 256);
#endif    	
    MD5 md5;
    md5.update((unsigned char*)&r, sizeof(randomness));
    md5.finalize();    
    memcpy(seed, md5.raw_digest(), 16);
}

//-------------------------------------------------------------------------------

void miniuuid::initial(unsigned char hash[16]) 
{
	/* Construct a version 3 uuid with the (pseudo-)random number plus a few constants. */

	memcpy(&muuid, hash, sizeof(util_uuid_t));

	/* convert UUID to local byte order */
	muuid.time_low = ntohl(muuid.time_low);
	muuid.time_mid = ntohs(muuid.time_mid);
	muuid.time_hi_and_version = ntohs(muuid.time_hi_and_version);

  /* put in the variant and version bits */
	muuid.time_hi_and_version &= 0x0FFF;
	muuid.time_hi_and_version |= (3 << 12);
	muuid.clock_seq_hi_and_reserved &= 0x3F;
	muuid.clock_seq_hi_and_reserved |= 0x80;
};

void miniuuid::initial(unsigned16 clock_seq, uuid_time_t timestamp, uuid_node_t node)
{
    /* Construct a version 1 uuid with the information we've gathered
     * plus a few constants. */
    muuid.time_low = (unsigned int)(timestamp & 0xFFFFFFFF);
    muuid.time_mid = (unsigned short)((timestamp >> 32) & 0xFFFF);
    muuid.time_hi_and_version = (unsigned short)((timestamp >> 48) & 0x0FFF);
    muuid.time_hi_and_version |= (1 << 12);
    muuid.clock_seq_low = clock_seq & 0xFF;
    muuid.clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8;
    muuid.clock_seq_hi_and_reserved |= 0x80;
    memcpy(&muuid.node, &node, sizeof(muuid.node));	
}

miniuuid::miniuuid()
{
    uuid_time_t timestamp, last_time;
    unsigned16 clockseq;
    uuid_node_t node;
    uuid_node_t last_node;
    int f;

    /* acquire system wide lock so we're alone */
    //LOCK;
    
    get_current_time(&timestamp); /* get current time */
    get_ieee_node_identifier(&node); /* get node ID */
    f = read_state(&clockseq, &last_time, &last_node);     /* get saved state from NV storage */
    /* if no NV state, or if clock went backwards, or node ID changed
       (e.g., net card swap) change clockseq */
    if (!f || memcmp(&node, &last_node, sizeof(uuid_node_t)))
        clockseq = true_random();
    else if (timestamp < last_time)
        clockseq++;
   
    initial(clockseq, timestamp, node); /* stuff fields into the UUID */
    write_state(clockseq, timestamp, node); /* save the state for next time */
    //UNLOCK;
	miniutil::thread::sleep(10);
}	

 /*  make a UUID from the timestamp, clockseq, and node ID */

miniuuid::miniuuid(unsigned16 clock_seq, uuid_time_t timestamp, uuid_node_t node)
{
	initial(clock_seq, timestamp, node);
}

/*  create a UUID using a "name" from a "name space" */
/* nsid: UUID to serve as context, so identical names from different name spaces generate different UUIDs */
/* name : the name from which to generate a UUID*/
/* namelen :the length of the name*/

miniuuid::miniuuid(util_uuid_t nsid, void * name, int namelen) 
{
	unsigned char hash[16];
	util_uuid_t net_nsid;      /* context UUID in network byte order */

	/* put name space ID in network byte order so it hashes the same
	  no matter what endian machine we're on */
	net_nsid = nsid;
	htonl(net_nsid.time_low);
	htons(net_nsid.time_mid);
	htons(net_nsid.time_hi_and_version);

	MD5 md5;
	md5.update((unsigned8*)&net_nsid, sizeof(util_uuid_t));
	md5.update((unsigned8*)name, namelen);
	md5.finalize();
	memcpy(hash, md5.raw_digest(), 16);

	/* the hash is in network byte order at this point */
	initial(hash);
};
  
miniuuid::miniuuid(unsigned char hash[16])
{
	initial(hash);
}


/* --  Compare two UUID's "lexically" and return
       -1   u1 is lexically before u2
        0   u1 is equal to u2
        1   u1 is lexically after u2
    Note:   lexical ordering is not temporal ordering!
*/
int miniuuid::compare(const miniuuid& other)
{
	util_uuid_t *u1 = &this->muuid;
	const util_uuid_t *u2 = &other.muuid;
	int i;

#define CHECK(f1, f2) if (f1 != f2) return f1 < f2 ? -1 : 1;
	CHECK(u1->time_low, u2->time_low);
	CHECK(u1->time_mid, u2->time_mid);
	CHECK(u1->time_hi_and_version, u2->time_hi_and_version);
	CHECK(u1->clock_seq_hi_and_reserved, u2->clock_seq_hi_and_reserved);
	CHECK(u1->clock_seq_low, u2->clock_seq_low)
	for (i = 0; i < 6; i++) {
	  if (u1->node[i] < u2->node[i])
		   return -1;
	  if (u1->node[i] > u2->node[i])
	  return 1;
	}
	return 0;
};


int miniuuid::get_uuid(util_uuid_t &refuuid)
{
	refuuid = muuid;
	return 0;
}
	
int miniuuid::get_uuid(char* pbuf, int len) // len must equal 16
{
	if(len < 16)
		return -1;
	memcpy(pbuf, &muuid, sizeof(muuid));
	return 0;
}	

//---------static function------------
int miniuuid::getuuid(util_uuid_t &refuuid)
{
	miniuuid auid;
	return auid.get_uuid(refuuid);
}

int miniuuid::getuuid(char* pbuf, int len)
{
	miniuuid auid;
	return auid.get_uuid(pbuf, len);
}



